---
title: Build xPression XML
summary: Serializes an XDesignXElement/XDesignXDocument tree into the xPression XML dialect, propagating keys for nested repeating sections.
status: draft
owner: SAIF Documentation
last_reviewed: 2026-09-01
library: SAIF.Services.Printing
reference_type: embedded
operation_id: op-11-build-xpression-xml
trigger: ToXpression() invoked (directly on an XDesignXDocument/XDesignXElement, or indirectly via ReportBase.Render)
---

# Build xPression XML

Serializes an `XDesignXElement`/`XDesignXDocument` tree into the xPression-dialect XML string consumed by the print/preview/distribute gateway, propagating primary and foreign keys down through nested repeating sections before rendering.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-11 |
| **Trigger** | `ToXpression()` invoked (directly on an `XDesignXDocument`/`XDesignXElement`, or indirectly via `ReportBase.Render`) |
| **Inputs** | An `XDesignXElement`/`XDesignXDocument` tree with keys/foreign keys set via `AddKey` |
| **Outputs** | xPression-dialect XML string |
| **Primary components** | `XDesignXDocument.ToXpression`, `XDesignXElement.ToXpression`, `PropagateKeys`, `RenderElement`/`RenderStartElement`/`RenderEndElement` |
| **Entry point** | `Public Function ToXpression() As String` (`XDesignXDocument.vb`); `Public Function ToXpression(Optional tabIndex As Integer = 0) As String` (`XDesignXElement.vb`) |

## Steps

1. `XDesignXDocument.ToXpression()` checks that `Me.Root` is not `Nothing` and that it is a `XDesignXElement` ([BR-PRT-019](../business-rules.md#br-prt-019-empty-serialization-for-malformed-documents)).
   - If either condition fails: returns an empty string immediately, without attempting any serialization.
2. Otherwise delegates to `CType(Me.Root, XDesignXElement).ToXpression()`, the element-level recursive serializer, starting at `tabIndex=0`.
3. `XDesignXElement.ToXpression(tabIndex)`, on the first (top-level) call only (`tabIndex=0`), invokes `PropagateKeys()` once for the whole tree before rendering begins ([BR-PRT-016](../business-rules.md#br-prt-016-key-propagation-for-nested-repeating-sections)).
   - `PropagateKeys` walks all descendant `XDesignXElement`s recursively: for each descendant element that itself has children, it clears that element's existing foreign keys, then adds every one of the current element's own primary keys plus every one of the current element's already-accumulated foreign keys as new foreign keys on the descendant, then recurses into the descendant.*
4. If the current element has no children (`Elements.Count = 0`): renders as a single leaf via `RenderElement(tabIndex, Name.LocalName, Value)`.
   - If `Value` is an empty string: emits a self-closing tag (`<Name/>`) instead of an empty-value pair of tags ([BR-PRT-017](../business-rules.md#br-prt-017-empty-value-self-closing-elements)).
   - If `Value` is non-empty: escapes it via `System.Security.SecurityElement.Escape` before emitting `<Name>escapedValue</Name>` ([BR-PRT-018](../business-rules.md#br-prt-018-xml-value-escaping)).
5. If the current element has children: renders a start tag via `RenderStartElement`, then for each accumulated foreign key emits it as a child leaf element via `RenderElement` (itself subject to [BR-PRT-017](../business-rules.md#br-prt-017-empty-value-self-closing-elements) / [BR-PRT-018](../business-rules.md#br-prt-018-xml-value-escaping)), then recurses into each real child element (`ToXpression(tabIndex+1)`).
   - Non-`XDesignXElement` children are first converted via the `ToSAIFXElement` extension method before recursing.
   - After all children are rendered, emits the end tag via `RenderEndElement`.
6. Returns the fully concatenated string up the recursion chain to the original `XDesignXDocument.ToXpression()` caller.

## Process flow

The following diagram visualizes this operation's control flow, from the document-level malformed-input guard through the element-level recursive serialization.

```mermaid
flowchart TD
	Start(["ToXpression called<br>on Document"]) --> Guard{"Root exists AND<br>is XDesignXElement?"}
	Guard -->|No| EndEmpty(["Return empty string"])
	Guard -->|Yes| Element["Element.ToXpression(0)"]
	Element --> TopCheck{"tabIndex = 0?"}
	TopCheck -->|Yes| Propagate["PropagateKeys() once"]
	TopCheck -->|No| Children
	Propagate --> Children{"Has children?"}
	Children -->|No| Leaf["RenderElement leaf"]
	Leaf --> ValueCheck{"Value empty?"}
	ValueCheck -->|Yes| SelfClose["Self-closing tag"]
	ValueCheck -->|No| Escape["Escape and emit<br>text node"]
	SelfClose --> EndLeaf(["Return leaf string"])
	Escape --> EndLeaf
	Children -->|Yes| StartTag["RenderStartElement"]
	StartTag --> Keys["Emit foreign keys<br>as leaf elements"]
	Keys --> Recurse["Recurse into each<br>child element"]
	Recurse --> EndTag["RenderEndElement"]
	EndTag --> EndBranch(["Return concatenated string"])
```

## Failure and edge-case behavior
- A document with no root, or whose root is not an `XDesignXElement`, serializes silently to an empty string — a caller cannot distinguish "empty document" from "malformed document" from the return value alone ([BR-PRT-019](../business-rules.md#br-prt-019-empty-serialization-for-malformed-documents)).
- `PropagateKeys` runs only once per top-level `ToXpression()` call, guarded by the `tabIndex=0` check. Calling `ToXpression(tabIndex:=N)` directly on a sub-element with `N > 0` skips key propagation entirely, which would leave that sub-tree's foreign keys stale or unpropagated if invoked out of the normal top-down sequence — an easy-to-misuse API surface.*
- `RenderElement`'s empty-value comparison performs no null-check on `Me.Value` before comparing it to `""`; this relies on `XElement.Value` never returning `Nothing`, a .NET framework guarantee rather than a printing-library rule.

## Related documentation
- [Index](../index.md)
- [Overview](../overview.md)
- [Architecture](../architecture.md)
- [Dependencies](../dependencies.md)
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [API reference](../api/index.md)

> **Note:** Statements marked with * were inferred from code structure, naming conventions, or
> contextual evidence where direct confirmation was not available in source artifacts. Statements
> marked with ** could not be confirmed; treat as unknown until verified.

## Sources
- `SAIF.Services.Printing/Reporting/XDesignXElement.vb`
- `SAIF.Services.Printing/Reporting/XDesignXDocument.vb`
